#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glu32.lib")

#include <windows.h>
#include <time.h>
#include "Win32Window.h"
#include "OnMouseMoveEvent.h"
#include "OnWindowResizeEvent.h"
#include "OnMouseLeftDownEvent.h"
#include "OnMouseLeftUpEvent.h"
#include "OnMouseRightDownEvent.h"
#include "OnKeyPressEvent.h"
#include "OnKeyReleaseEvent.h"

int Win32Window::Callback(unsigned int uMsg, unsigned int wParam, long lParam)
{
	switch (uMsg)
	{
	case WM_ACTIVATE:							// Watch For Window Activate Message
		{
			if (!HIWORD(wParam))					// Check Minimization State
			{
				m_active = true;						// Program Is Active
			} else
			{
				m_active = false;						// Program Is No Longer Active
			}
			return 0;								// Return To The Message Loop
		};
	case WM_SYSCOMMAND:
		{
			switch (wParam)
			{
				case SC_SCREENSAVE:
				case SC_MONITORPOWER:
					return 0;
			}
			break;
		};
	case WM_CLOSE:								// Did We Receive A Close Message?
		{
			PostQuitMessage(0);						// Send A Quit Message
			return 0;								// Jump Back
		};
	case WM_KEYDOWN:							// Is A Key Being Held Down?
		{
			UserInput::keys[wParam] = true;					// If So, Mark It As TRUE
			OnKeyPressEvent event(wParam, true);
			ReceiveMessage(event);
			return 0;								// Jump Back
		};
	case WM_CHAR:								// Textual Character Message
		{
			OnKeyPressEvent event(wParam, false);
			ReceiveMessage(event);
			return 0;								// Jump Back
		};

	case WM_KEYUP:								// Has A Key Been Released?
		{
			UserInput::keys[wParam] = false;					// If So, Mark It As FALSE
			OnKeyReleaseEvent event(wParam, true);
			ReceiveMessage(event);
			return 0;								// Jump Back
		};
	case WM_MOUSEMOVE:
		{
			POINTS p = MAKEPOINTS(lParam);
			OnMouseMoveEvent event(p.x, p.y);
			
			event.Movement() = Maths::Point<>(p.x - UserInput::mousePosition.X, p.y - UserInput::mousePosition.Y);
			UserInput::mousePosition = Maths::Point<>(p.x, p.y);
			event.ControlKey() = MK_CONTROL & wParam;
			event.ShiftKey() = MK_SHIFT & wParam;
			event.LButton() = MK_LBUTTON & wParam;
			event.RButton() = MK_RBUTTON & wParam;

			this->ReceiveMessage(event);
			// buttons, dx, dy
			return 0;
		};
	case WM_LBUTTONUP:
		{
			POINTS p = MAKEPOINTS(lParam);
			UserInput::mousePosition = Maths::Point<>(p.x, p.y);
			if(m_mouseDown)
			{
				OnMouseLeftUpEvent event(p.x, p.y);
			
				event.ControlKey() = MK_CONTROL & wParam;
				event.ShiftKey() = MK_SHIFT & wParam;

				this->ReceiveMessage(event);
				m_mouseDown = false;
			}
			// buttons, dx, dy
			return 0;
		};
	case WM_LBUTTONDOWN:
		{
			POINTS p = MAKEPOINTS(lParam);
			UserInput::mousePosition = Maths::Point<>(p.x, p.y);
			if(!m_mouseDown)
			{
				OnMouseLeftDownEvent event(p.x, p.y);
			
				event.ControlKey() = MK_CONTROL & wParam;
				event.ShiftKey() = MK_SHIFT & wParam;

				this->ReceiveMessage(event);
				m_mouseDown = true;
			}
			// buttons, dx, dy
			return 0;
		};
	case WM_RBUTTONDOWN:
		{
			POINTS p = MAKEPOINTS(lParam);
			UserInput::mousePosition = Maths::Point<>(p.x, p.y);
			
			OnMouseRightDownEvent event(p.x, p.y);
			
			event.ControlKey() = MK_CONTROL & wParam;
			event.ShiftKey() = MK_SHIFT & wParam;

			this->ReceiveMessage(event);
			
			// buttons, dx, dy
			return 0;
		};
	case WM_SIZE:								// Resize The OpenGL Window
		{
			// POST RESIZEEVENT TO EVENTMANAGER 
			Maths::Point<int> size(LOWORD(lParam),HIWORD(lParam));    // LoWord=Width, HiWord=Height
			m_size = size;
			this->ReceiveMessage(OnWindowResizeEvent(size));
			return 0;								// Jump Back
		};
	//case WM_NETWORKMESSAGE:
	//	{
		//	EventManager->VQueueEvent( new CNetworkEvent( wParam, lParam ) );
			// POST NETWORKEVENT
			// SOCKET = wParam
			// INFO = lParam
	//		return 0;
	//	}
	}
	return DefWindowProc((HWND)m_handle, uMsg,wParam,lParam);
}

LRESULT CALLBACK proc( HWND hWnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam )
{
	IWindow* window = 0;

	if(uMsg == WM_CREATE)
	{
		window = (IWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;
		SetWindowLongPtr(hWnd, GWL_USERDATA,(LONG_PTR)window);
	} else
	{
		window = (IWindow*)GetWindowLongPtr(hWnd,GWL_USERDATA);
		if(!window)
			return DefWindowProc(hWnd,uMsg,wParam,lParam);
	}

	return window->Callback(uMsg, wParam, lParam);
}

void Win32Window::Messages()
{
	MSG msg;
	while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE ))
	{
		if(msg.message == WM_QUIT)
		{
			m_quit = true;
		} else 
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
}

bool Win32Window::Create( const char* title, int w, int h, bool fullscreen )
{
	wchar_t    wszDest[256];
	MultiByteToWideChar(0,0, title, -1, wszDest, 256);

	WNDCLASS wc;
	unsigned int dwExStyle;
	unsigned int dwStyle;

	m_instance			= GetModuleHandle(NULL);
	
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	
	wc.lpfnWndProc		= (WNDPROC) proc;						
	wc.cbClsExtra		= 0;									
	wc.cbWndExtra		= 0;									
	wc.hInstance		= (HINSTANCE)m_instance;				
	wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);			
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);			
	wc.hbrBackground	= NULL;									
	wc.lpszMenuName		= NULL;									
	wc.lpszClassName	= L"baseWindow";						

	RECT rect;
	rect.top = 0;
	rect.bottom = h;
	rect.left = 0;
	rect.right = w;

	if (!RegisterClass(&wc))
	{
		// RegisterClass failes notify user
		MessageBox(NULL,L"Failed To Register Base Window Class.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;											// Return FALSE
	}

	if( fullscreen )
	{
		DEVMODE dmScreenSettings;								// Device Mode
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	// Makes Sure Memory's Cleared
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);		// Size Of The Devmode Structure
		dmScreenSettings.dmPelsWidth	= w;			// Selected Screen Width
		dmScreenSettings.dmPelsHeight	= h;			// Selected Screen Height
		dmScreenSettings.dmBitsPerPel	= 16;			// Selected Bits Per Pixel
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			if (MessageBox(NULL,L"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?",L"Window",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
			{
				fullscreen=false;
			}
			else
			{
				MessageBox(NULL,L"Program Cannot Initialize Window.",L"ERROR",MB_OK|MB_ICONSTOP);
				return false;									// Return FALSE
			}
		}
	}
	if (fullscreen)												
	{
		dwExStyle	=	WS_EX_APPWINDOW;
		dwStyle		=	WS_POPUP;
		ShowCursor(false);
	}
	else
	{
		dwExStyle	=	WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;	
		dwStyle		=	WS_OVERLAPPEDWINDOW;
	}

	AdjustWindowRectEx(&rect, dwStyle, FALSE, dwExStyle);

	if (!(m_handle = CreateWindowEx(	dwExStyle,							// Extended Style For The Window
										L"baseWindow",						// Class Name
										wszDest,							// Window Title
										dwStyle |							// Defined Window Style
										WS_CLIPSIBLINGS |					// Required Window Style
										WS_CLIPCHILDREN,					// Required Window Style
										0, 0,								// Window Position
										rect.right-rect.left,				// Calculate Window Width
										rect.bottom-rect.top,				// Calculate Window Height
										NULL,								// No Parent Window
										NULL,								// No Menu
										(HINSTANCE)m_instance,				// Instance
										this)))								// Dont Pass Anything To WM_CREATE
	{
		Destroy();
		MessageBox(NULL,L"Window Creation Failed.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}
	m_fullscreen = fullscreen;
	ShowWindow((HWND)m_handle,SW_SHOW);
	SetForegroundWindow((HWND)m_handle);
	SetFocus((HWND)m_handle);	

	m_size.X = w;	
	m_size.Y = h;
	m_quit = false;
	return true;
}

void Win32Window::Destroy()
{
	if (m_fullscreen)										// Are We In Fullscreen Mode?
	{
		ChangeDisplaySettings(NULL,0);					// If So Switch Back To The Desktop
		ShowCursor(true);								// Show Mouse Pointer
	}

	if (m_handle && !DestroyWindow((HWND)m_handle))					// Are We Able To Destroy The Window?
	{
		MessageBox(NULL,L"Could Not Release hWnd.",L"SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		m_handle = NULL;										// Set hWnd To NULL
	}

	if (!UnregisterClass(L"baseWindow",(HINSTANCE)m_instance))			// Are We Able To Unregister Class
	{
		MessageBox(NULL,L"Could Not Unregister Class.",L"SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		m_instance = NULL;									// Set hInstance To NULL
	}
}